package io.netty.handler.codec;

import com.util.MyUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import java.util.List;

/**
 * GeorgeCaoJ Created in 2017.12.05
 * Class for making ip packet completable and check with XOR
 * Complete Rule:
 * 1. check HEADER
 * 2. take bytes by length byte
 * 3. check the XOR result
 *
 * ps:  LengthFieldBasedFrameDecoder object used in class and invoke it's protected method
 * must used in the same package if not it's subclass;
 */

public class CompleteFrameHandler extends ByteToMessageDecoder{
    /**
     * @param maxFrameLength
     *        the maximum length of the frame.  If the length of the frame is
     *        greater than this value, {@link TooLongFrameException} will be
     *        thrown.
     * @param lengthFieldOffset
     *        the offset of the length field
     * @param lengthFieldLength
     *        the length of the length field
     * @param lengthAdjustment
     *        the compensation value to add to the value of the length field
     * @param initialBytesToStrip
     *        the number of first bytes to strip out from the decoded frame
     */
    private final static int maxFrameLength = 256;
    private final static int lengthFieldOffset = 2;
    private final static int lengthFieldLength = 1;
    private final static int lengthAdjustment = 0;
    private final static int initialBytesToStrip = 0;

    private final ByteBuf HEADER = Unpooled.copiedBuffer(new byte[]{(byte)0x7F, (byte)0xF7});

    private final LengthFieldBasedFrameDecoder lengthFieldBasedFrameDecoder = new LengthFieldBasedFrameDecoder(maxFrameLength,
            lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);

    @Override
    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception{
        int headerIndex = indexOf(in, HEADER);
        if (headerIndex == 0){  // HEADER is at head
            Object decoded = lengthFieldBasedFrameDecoder.decode(ctx, in);
            if (decoded != null && MyUtil.xorCheck((ByteBuf)decoded))
                out.add(decoded);
        }
        else if (headerIndex > 0){ // HEADER is not at head
            in.skipBytes(headerIndex);
        }
    }





    /**
     * Returns the number of bytes between the readerIndex of the haystack and
     * the first needle found in the haystack.  -1 is returned if no needle is
     * found in the haystack.
     */
    private static int indexOf(ByteBuf haystack, ByteBuf needle) {
        for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) {
            int haystackIndex = i;
            int needleIndex;
            for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) {
                if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) {
                    break;
                } else {
                    haystackIndex ++;
                    if (haystackIndex == haystack.writerIndex() &&
                            needleIndex != needle.capacity() - 1) {
                        return -1;
                    }
                }
            }

            if (needleIndex == needle.capacity()) {
                // Found the needle from the haystack!
                return i - haystack.readerIndex();
            }
        }
        return -1;
    }

}
